\documentclass{article}
\usepackage[UTF8]{ctex} % 中文支持
\usepackage{geometry}
\usepackage{hyperref}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{graphicx} % 添加图片支持
\usepackage{float} % 添加浮动控制
\geometry{a4paper, margin=2.5cm}

\lstset{
  basicstyle=\ttfamily\small,
  breaklines=true,
  backgroundcolor=\color{gray!10},
  frame=single,
  columns=flexible
}

\title{水泥流动性识别系统搭建开发}
\author{dxymie}
\date{June 2025}

\begin{document}

\maketitle
\pagenumbering{arabic}

\section{引言}

本项目基于 Rockchip RK3566 平台，搭建用于水泥流动性识别的嵌入式系统。系统需定制 HDMI 输出、串口配置、IO 设置与网络接口。

\section{准备工作}

\begin{itemize}
  \item 系统：Ubuntu 18.04+
  \item 工具：Docker
  \item SDK 包：\texttt{tc-rk3566-linux-release.tar.bz2}
  \item 我们重新构建的 Docker 镜像：\texttt{android11-sdk-cmp.tar}
\end{itemize}

\textbf{重要注意事项：}
\begin{enumerate}
  \item 不要使用 WSL（Windows Subsystem for Linux）进行开发，因为 Windows 文件系统不区分大小写，会导致文件合并问题。
  \item 编译 linuxsdk 时，linuxsdk 目录内容无需修改！！！
\end{enumerate}

\section{系统搭建步骤}

\subsection{解压与权限设置}

首先解压系统文件并设置权限：

\begin{lstlisting}[language=bash]
# 解压系统文件
tar xjvf tc-rk3566-linux-release.tar.bz2

# 设置权限
chmod 777 -R tc-rk3566-linux-release
\end{lstlisting}

\subsection{Docker 环境配置}

导入并运行 Docker 镜像：

\begin{lstlisting}[language=bash]
# 导入 Docker 镜像
docker load -i android11_sdk_cmp.tar

# 运行 Docker 容器
docker run --privileged \
  --mount type=bind,source=/home/book/tc-rk3566-linux-release,target=/home/lckfb \
  --name="lckfb_rk3566_dev" \
  -h lckfb \
  -it new_android11_sdk_cmp:v1 /bin/bash
\end{lstlisting}

注意：source 参数需要替换为实际的文件夹路径，target 参数保持不变。

\subsection{系统编译}

进入 Docker 容器后，按照以下步骤进行系统编译：

\begin{enumerate}
  \item 初始化编译环境：
  \begin{lstlisting}[language=bash]
  ./build.sh init
  \end{lstlisting}
  
  \item 在菜单中选择编译选项：（选择选项 1）
  \begin{itemize}
    \item 选择选项 1：BoardConfig-tc-box-rk3566-buildroot.mk
    \item 选择选项 2：BoardConfig-tc-box-rk3566-debian.mk
    \item 选择选项 3：BoardConfig-tc-box-rk3566-ubuntu.mk
  \end{itemize}
  
  \item 执行首次编译（耗时较长）：
  \begin{lstlisting}[language=bash]
  ./build.sh
  \end{lstlisting}
\end{enumerate}

首次编译结果如图 \ref{fig:first-build} 所示：

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{images/首次编译结果.png}
  \caption{首次编译结果}
  \label{fig:first-build}
\end{figure}

注意：linuxsdk 目录内容无需修改。默认编译完成后为 LCD 输出，调试串口功能开启。

\subsection{设备树修改}

修改设备树文件：

\begin{enumerate}
  \item 进入设备树目录：
  \begin{lstlisting}[language=bash]
  cd kernel/arch/arm64/boot/dts/rockchip/
  \end{lstlisting}
  
  \item 替换以下文件：
  \begin{itemize}
    \item tc-box-rk3566.dts
  \end{itemize}
\end{enumerate}




\begin{enumerate}
  \item 解决权限问题：
  \begin{lstlisting}[language=bash]
  cd ..
  sudo chmod 777 -R /lckfb
  cd lckfb
  ./build.sh init
  \end{lstlisting}
\end{enumerate}

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{images/权限问题报错.png}
  \caption{权限问题报错}
  \label{fig:permission-error}
\end{figure}

选择 Ubuntu 编译选项（选项 3）并等待编译完成。编译完成后，生成的文件位于 rockdev 目录下：

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{images/编译后生成结果.png}
  \caption{编译后生成结果}
  \label{fig:build-result}
\end{figure}

\subsection{系统烧录与启动}

\subsubsection{镜像烧录流程}

首先按照以下步骤烧录固件 \texttt{update-tc-box-rk3566-ubuntu-1804-20250427-020903HDMI.img}：

\begin{enumerate}
  \item 烧录步骤：
  \begin{itemize}
    \item 按住 recovery 键不松
    \item 按一下 reset 键
    \item 三秒后松开
  \end{itemize}
\end{enumerate}

\begin{figure}[H]
  \centering
  \includegraphics[width=0.8\textwidth]{images/镜像烧录截图.png}
  \caption{镜像烧录过程}
  \label{fig:burn-process}
\end{figure}

重新上电并进入烧录模式，依次烧录：
\begin{itemize}
  \item 设备树
  \item 根文件系统
  \item 参数文件
\end{itemize}

\begin{figure}[H]
  \centering
  \includegraphics[width=0.8\textwidth]{images/根文件设备树依次烧录截图.png}
  \caption{根文件系统、设备树依次烧录}
  \label{fig:burn-files}
\end{figure}

\section{系统启动配置}

\begin{enumerate}
  \item 启动配置：
  \begin{itemize}
    \item 插入 USB 转网口模块
    \item 启动开发板
    \item 获取 IP 地址
  \end{itemize}
\end{enumerate}

\begin{figure}[H]
  \centering
  \includegraphics[width=0.8\textwidth]{images/路由器有线连接获取ip截图.png}
  \caption{获取 IP 地址配置}
  \label{fig:ip-config}
\end{figure}

通过 SSH 连接系统：

\begin{figure}[H]
  \centering
  \includegraphics[width=0.8\textwidth]{images/ssh连接结果.png}
  \caption{SSH 连接结果}
  \label{fig:ssh-connect}
\end{figure}

\section{DTS 配置说明}
\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{images/dts_GPIO配置.png}
  \caption{dts GPIO配置}
  \label{fig:dts_GPIO配置}
\end{figure}
在设备树中，主要配置 GPIO 控制器的属性如图\ref{fig:dts_GPIO配置}：


\begin{itemize}
  \item \texttt{compatible = "rockchip,gpio-bank"}: 标识硬件为 Rockchip GPIO 控制器
  \item \texttt{reg = <0x0 0xfe770000 0x0 0x100>}: 指定内存映射寄存器地址范围
  \item \texttt{interrupts = <GIC SPI 37 IRQ\_TYPE\_LEVEL\_HIGH>}: 配置中断，使用 GIC 中断控制器
  \item \texttt{clocks = <\&cru PCLK\_GPIO4>, <\&cru DBCLK\_GPIO4>}: 指定时钟源
  \item \texttt{gpio-controller}: 标记为 GPIO 控制器
  \item \texttt{\#gpio-cells = <2>}: 定义 GPIO 引脚需要两个单元（引脚号和标志）
  \item \texttt{gpio-ranges = <\&pinctrl 0 128 32>}: 映射 GPIO 编号到引脚控制器范围
  \item \texttt{interrupt-controller}: 标记为中断控制器
  \item \texttt{\#interrupt-cells = <2>}: 定义中断需要两个单元
\end{itemize}

这些配置用于设置引脚的上拉下拉输入特性，
具体配置方法可参考文档：
file:///E:/rk3566/docs/Common/Pin-Ctrl/Rockchip\_Problem\_Shooting\_Linux\_GPIO\_CN.pdf

\subsection{串口配置}

\subsubsection{关闭 Debug 串口}

在 \texttt{rk3568-linux.dtsi} 文件中，找到 fiq-debugger 节点并修改：

\begin{lstlisting}[language=bash]
fiq-debugger {
        compatible = "rockchip,fiq-debugger";
        // status = "disabled";
        rockchip,serial-id = <2>;
        rockchip,wake-irq = <0>;
        /* If enable uart uses irq instead of fiq */
        rockchip,irq-mode-enable = <1>;
        rockchip,baudrate = <115200>;  /* Only 115200 and 1500000 */
        interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_LOW>;
        pinctrl-names = "default";
        pinctrl-0 = <&uart2m0_xfer>;
        status = "disabled";  // 修改为 disabled
};
\end{lstlisting}

配置说明：
\begin{itemize}
  \item \texttt{status = "disabled"}: 禁用 debug 串口功能
  \item \texttt{rockchip,serial-id = <2>}: 指定串口 ID 为 2
  \item \texttt{rockchip,baudrate = <115200>}: 波特率设置为 115200
  \item \texttt{pinctrl-0 = <\&uart2m0\_xfer>}: 指定串口引脚配置
\end{itemize}

\subsubsection{启用普通串口}

在 \texttt{tc-box-rk3566.dts} 文件中添加串口 2 的使能配置：

\begin{lstlisting}[language=bash]
&uart2 {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&uart2m0_xfer>;
};
\end{lstlisting}

配置说明：
\begin{itemize}
  \item \texttt{status = "okay"}: 启用串口 2
  \item \texttt{pinctrl-names = "default"}: 引脚控制名称
  \item \texttt{pinctrl-0 = <\&uart2m0\_xfer>}: 使用默认的串口引脚配置
\end{itemize}

\subsubsection{串口测试}

配置完成后，可以通过以下方式测试串口：

\begin{enumerate}
  \item 查看串口设备：
  \begin{lstlisting}[language=bash]
  ls /dev/ttyS*
  \end{lstlisting}
  
  \item 测试串口通信：
  \begin{lstlisting}[language=bash]
  # 发送数据
  echo "test" > /dev/ttyS2
  
  # 接收数据
  cat /dev/ttyS2
  \end{lstlisting}
  
  \item 使用串口工具：
  \begin{lstlisting}[language=bash]
  # 安装串口工具
  sudo apt-get install minicom
  
  # 配置串口
  sudo minicom -s
  \end{lstlisting}
\end{enumerate}

注意事项：
\begin{itemize}
  \item 修改设备树后需要重新编译内核
  \item 确保串口引脚配置正确
  \item 波特率设置要与外部设备匹配
  \item 注意串口权限设置
\end{itemize}

\subsubsection{串口操作数码管}

创建串口操作数码管的 Python 脚本 \texttt{digital\_tube\_control.py}：

\begin{lstlisting}[language=python]
import serial
import time

# 串口配置（需根据实际端口修改）
SERIAL_PORT = 'COM7'  # Windows系统可能为'COM3'等
BAUD_RATE = 9600
TIMEOUT = 1

# 数值显示模式指令构造函数
def create_numeric_command(value, brightness=8, decimal_places=0, is_negative=False):
    """
    生成数值显示模式的通讯指令
    :param value: 显示数值（需为整数，范围：-32768 ~ 32767）
    :param brightness: 亮度等级（1-8级，默认8级）
    :param decimal_places: 小数位数（0-3，默认0）
    :param is_negative: 是否为负数（默认False）
    :return: 指令字节数组
    """
    # 校验亮度和小数位数范围
    if not (1 <= brightness <= 8):
        raise ValueError("亮度等级需为1-8")
    if not (0 <= decimal_places <= 3):
        raise ValueError("小数位数需为0-3")
    
    # 处理正负号
    if is_negative:
        value = -value
        sign_byte = 0x01  # 负数标志
    else:
        sign_byte = 0x00
    
    # 拆分16位数值为高低字节（小端模式）
    high_byte = (value >> 8) & 0xFF
    low_byte = value & 0xFF
    
    # 构造指令字：高4位为亮度（0x10~0x80），低4位为模式0（数值显示）
    command_byte = (brightness << 4) | 0x00  # 数值显示模式对应低4位0x0
    
    # 计算校验和（前6字节累加取低8位）
    checksum = (0x5F + command_byte + high_byte + low_byte + decimal_places + sign_byte) & 0xFF
    
    # 组合完整指令（7字节）
    return bytes([
        0x5F,          # 识别字
        command_byte,  # 指令字
        high_byte,     # 数值高8位
        low_byte,      # 数值低8位
        decimal_places,# 小数位数
        sign_byte,     # 正负号
        checksum       # 校验和
    ])

# 主程序：循环发送数据
if __name__ == "__main__":
    # 初始化串口
    try:
        ser = serial.Serial(
            port=SERIAL_PORT,
            baudrate=BAUD_RATE,
            timeout=TIMEOUT,
            bytesize=serial.EIGHTBITS,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE
        )
        print(f"已连接串口：{SERIAL_PORT}")
    except serial.SerialException as e:
        print(f"串口连接失败：{e}")
        exit(1)
    
    # 待显示的数值列表（均为正数，无小数位）
    display_values = [000,150, 160, 170, 180, 190,200]
    
    try:
        while True:
            for value in display_values:
                # 生成指令（8级亮度，0位小数，正数）
                cmd = create_numeric_command(value, brightness=8, decimal_places=0)
                ser.write(cmd)  # 发送指令
                print(f"已发送：{value}，指令：{cmd.hex()}")
                
                # 等待1秒
                time.sleep(1)
                
    except KeyboardInterrupt:
        print("\n程序终止，关闭串口...")
        ser.close()
        exit(0)
\end{lstlisting}

代码功能说明：
\begin{itemize}
  \item \texttt{create\_numeric\_command()}: 构造数值显示指令
  \item 支持亮度调节（1-8级）
  \item 支持小数位数设置（0-3位）
  \item 支持正负数显示
  \item 自动计算校验和
  \item 循环显示预设数值
\end{itemize}

配置参数说明：
\begin{itemize}
  \item \texttt{SERIAL\_PORT}: 串口端口号（Windows 为 COM 系列）
  \item \texttt{BAUD\_RATE}: 波特率（9600）
  \item \texttt{TIMEOUT}: 超时时间（1秒）
  \item \texttt{display\_values}: 待显示的数值列表
\end{itemize}

使用方法：
\begin{enumerate}
  \item 安装依赖：\texttt{pip3 install pyserial}
  \item 修改串口端口号（根据实际情况）
  \item 运行脚本：\texttt{python3 digital\_tube\_control.py}
  \item 观察数码管显示效果
\end{enumerate}

注意事项：
\begin{itemize}
  \item 确保串口连接正常
  \item 检查波特率设置
  \item 注意数值范围限制（-32768 ~ 32767）
  \item 可根据需要修改显示数值和间隔时间
\end{itemize}

\subsection{GPIO 按键配置}

配置按键输入功能，在设备树中添加以下配置：

\begin{lstlisting}[language=bash]
gpio_keys: gpio-keys {
        compatible = "gpio-keys";
        pinctrl-names = "default";
        pinctrl-0 = <&gpio4c1_input>;

        key1 {
                label = "gpio-key-test";
                linux,code = <KEY_F14>;
                gpios = <&gpio4 17 GPIO_ACTIVE_LOW>; // 17 = PC1
        };
};
\end{lstlisting}

配置说明：
\begin{itemize}
  \item \texttt{compatible = "gpio-keys"}: 标识为 GPIO 按键设备
  \item \texttt{pinctrl-names = "default"}: 引脚控制名称
  \item \texttt{pinctrl-0 = <\&gpio4c1\_input>}: 指定引脚配置
  \item \texttt{label = "gpio-key-test"}: 按键标签
  \item \texttt{linux,code = <KEY\_F14>}: 按键代码，对应 F14 键
  \item \texttt{gpios = <\&gpio4 17 GPIO\_ACTIVE\_LOW>}: GPIO 配置，使用 gpio4 的第 17 引脚，低电平有效
\end{itemize}

\subsection{按键监听脚本}

\subsubsection{Bash 脚本版本}

创建按键监听脚本 \texttt{key\_monitor.sh}：

\begin{lstlisting}[language=bash]
#!/bin/bash

# 监听 /dev/input/event5 的按键事件
evtest /dev/input/event5 | while read -r line; do
    if [[ "$line" == *"(KEY_F14), value 1"* ]]; then
        echo "检测到 KEY_F14 按下"
        # 在此执行自定义操作（如触发脚本）
    elif [[ "$line" == *"(KEY_F14), value 0"* ]]; then
        echo "检测到 KEY_F14 释放"
    fi
done
\end{lstlisting}

\subsubsection{Python 脚本版本}

创建更高级的 Python 按键监听脚本 \texttt{key\_monitor.py}：

\begin{lstlisting}[language=python]
#!/usr/bin/env python3
from evdev import InputDevice, categorize, ecodes, KeyEvent, list_devices
import sys
import os
import time

# 配置参数
DEVICE_NAME = "gpio-keys"   # 设备树中定义的输入设备名称
KEY_CODE = ecodes.KEY_F14  # 监听的按键码 (KEY_F14)
DEBOUNCE_TIME = 0.05        # 防抖时间 (单位: 秒)

def find_device(device_name):
    """通过设备名称自动查找输入设备路径"""
    for dev_path in list_devices():
        device = InputDevice(dev_path)
        if device.name == device_name:
            return dev_path
    raise FileNotFoundError(f"未找到设备: {device_name}")

def on_key_press():
    """按键按下时的回调函数 (可自定义)"""
    print("执行操作: 按键按下")
    # 示例: 执行 shell 命令
    # os.system("echo '按键按下' > /tmp/key.log")

def on_key_release():
    """按键释放时的回调函数 (可自定义)"""
    print("执行操作: 按键释放")
    # 示例: 播放提示音
    # os.system("spd-say '按键已释放'")

def main():
    try:
        # 自动检测设备路径
        device_path = find_device(DEVICE_NAME)
        device = InputDevice(device_path)
        
        # 打印设备信息
        print(f"监听设备: {device.name}")
        print(f"设备路径: {device_path}")
        print(f"物理地址: {device.phys}")
        print("按下 Ctrl+C 停止监听...")

        # 初始化防抖变量
        last_event_time = 0
        last_state = None

        # 事件监听循环
        for event in device.read_loop():
            if event.type == ecodes.EV_KEY and event.code == KEY_CODE:
                current_time = time.time()
                
                # 防抖处理
                if current_time - last_event_time < DEBOUNCE_TIME:
                    continue
                last_event_time = current_time
                
                # 解析按键状态
                key_event = categorize(event)
                new_state = key_event.keystate
                
                # 仅处理状态变化
                if new_state != last_state:
                    last_state = new_state
                    
                    if new_state == KeyEvent.key_down:
                        print(f"[{event.timestamp():.3f}] KEY_F14 按下")
                        on_key_press()
                    elif new_state == KeyEvent.key_up:
                        print(f"[{event.timestamp():.3f}] KEY_F14 释放")
                        on_key_release()

    except PermissionError:
        print("\n错误: 权限不足，请将当前用户加入 input 组：")
        print("  sudo usermod -aG input $USER && sudo reboot")
    except FileNotFoundError as e:
        print(f"\n错误: {e}")
        print("可能原因: 设备树未加载或设备名称不匹配")
    except KeyboardInterrupt:
        print("\n监听已停止")
    except Exception as e:
        print(f"\n未知错误: {str(e)}")

if __name__ == "__main__":
    # 检查是否以 root 身份运行
    main()
\end{lstlisting}

Python 脚本功能特点：
\begin{itemize}
  \item 自动检测设备路径，无需手动指定
  \item 内置防抖机制，避免按键抖动
  \item 支持自定义回调函数
  \item 详细的错误处理和权限检查
  \item 时间戳记录，便于调试
\end{itemize}

脚本配置参数说明：
\begin{itemize}
  \item \texttt{DEVICE\_NAME}: 设备树中定义的输入设备名称
  \item \texttt{KEY\_CODE}: 监听的按键码（KEY\_F14）
  \item \texttt{DEBOUNCE\_TIME}: 防抖时间（0.05秒）
\end{itemize}

使用方法：
\begin{enumerate}
  \item 安装依赖：\texttt{pip3 install evdev}
  \item 给脚本添加执行权限：\texttt{chmod +x key\_monitor.py}
  \item 运行脚本：\texttt{python3 key\_monitor.py}
  \item 按下配置的按键测试功能
\end{enumerate}

注意事项：
\begin{itemize}
  \item 需要将用户加入 input 组：\texttt{sudo usermod -aG input \$USER}
  \item 可能需要重启系统使权限生效
  \item 确保设备树已正确加载
\end{itemize}

\section{网口测试}

\subsection{网络端口状态检查}

在路由器上观察网口指示灯状态：
\begin{itemize}
  \item 黄色和绿色指示灯闪烁表示正常
  \item 黄色指示灯不亮可能表示焊接不良
  \item 绿色指示灯闪烁表示通信正常
\end{itemize}

\subsection{系统网络测试}

在启动界面连接键盘，按 \texttt{Ctrl+Alt+T} 打开命令行，执行以下命令测试网络连接：

\begin{lstlisting}[language=bash]
ping www.baidu.com
\end{lstlisting}

如果显示不工作，可检查串口确认系统是否正常启动。

\subsection{Ubuntu 系统网络诊断}

在 Ubuntu 系统中测试网口功能，使用以下命令：

\begin{enumerate}
  \item \texttt{ifconfig}: 检查网卡是否正确安装并处于 "running" 状态
  \item \texttt{ip addr}: 查看详细网口信息，包括 IP 地址、子网掩码和 MAC 地址
  \item \texttt{ip link}: 启用或禁用网络接口
\end{enumerate}

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{images/网口测试.png}
  \caption{网口测试配置}
  \label{fig:network-test}
\end{figure}

\section{总结}

本项目完成基于 RK3566 平台的系统构建，为后续的图像识别算法提供稳定系统支持。系统支持 HDMI 输出、串口通信、IO 控制和网络连接等功能。

\end{document}
